package com.demo.util;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

public class JacksonJsonUtils {
    private static Logger logger = LoggerFactory.getLogger(JacksonJsonUtils.class);
    private static final ObjectMapper objectMapper;
    static {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        configureBuilder(builder);
        objectMapper = builder.build();

    }

    private JacksonJsonUtils() {}

    public static void configureBuilder(Jackson2ObjectMapperBuilder builder) {
        builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateContants.FULL_TIME_FORMATTER))
            .serializerByType(LocalDate.class, new LocalDateSerializer(DateContants.YEAR_MONTH_DAY_FORMATTER))
            .serializerByType(LocalTime.class, new LocalTimeSerializer(DateContants.TIME_FORMATTER))
            .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateContants.FULL_TIME_FORMATTER))
            .deserializerByType(LocalDate.class, new LocalDateDeserializer(DateContants.YEAR_MONTH_DAY_FORMATTER))
            .deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateContants.TIME_FORMATTER))
//            .serializerByType(Long.class, ToStringSerializer.instance)
//            .serializerByType(BigInteger.class, ToStringSerializer.instance)
//            .serializerByType(BigDecimal.class, ToStringSerializer.instance)
            .dateFormat(new SimpleDateFormat(DateContants.DEFAULT_DATE_TIME_FORMAT))
            .featuresToEnable(MapperFeature.PROPAGATE_TRANSIENT_MARKER)// 忽略 transient 修饰的属性
            .featuresToEnable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
            // 该特性决定parser是否允许JSON字符串包含非引号控制字符（值小于32的ASCII字符，包含制表符和换行符）。
            // 如果该属性关闭，则如果遇到这些字符，则会抛出异常。JSON标准说明书要求所有控制符必须使用引号，因此这是一个非标准的特性
            // .featuresToEnable(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS)
            // .featuresToEnable(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)// 忽略不能转移的字符
            .featuresToEnable(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature())
            .featuresToEnable(JsonParser.Feature.ALLOW_SINGLE_QUOTES)// 单引号处理
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .featuresToDisable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
            .featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)// 忽略未知字段
            .serializationInclusion(JsonInclude.Include.NON_NULL);
    }

    public static ArrayNode getArrayNode() {
        return objectMapper.createArrayNode();
    }

    public static ObjectNode getObjectNode() {
        return objectMapper.createObjectNode();
    }

    /**
     * 通过path选择JsonNode 可能返回一个MissingNode
     * 
     * @param json {"name":"Jack","age":20,"class":["1","2","3"],"father":{"name":"old Jack","age":50}} father/name
     *            可以选择到old Jack class/1 可以选举到"2"
     * @return
     * @throws Exception
     */
    public static <T> T treeNodeToObject(JsonNode node, Class<T> classes) {
        T result = null;
        try {
            if (node != null && !node.isMissingNode() && !node.isNull()) {
                result = objectMapper.treeToValue(node, classes);
            }
        } catch (Exception e) {
            logger.error("treeNodeToObject error:", e);
        }
        return result;
    }

    /**
     * 通过path选择JsonNode 可能返回一个MissingNode
     * 
     * @param json {"name":"Jack","age":20,"class":["1","2","3"],"father":{"name":"old Jack","age":50}} father/name
     *            可以选择到old Jack class/2 可以选举到"2"
     * @return
     * @throws Exception
     */
    public static JsonNode pathSelectNode(JsonNode node, String path) {
        JsonNode result = node;
        try {
            if (StringUtils.isBlank(path)) {
                return node;
            }
            String[] paths = StringUtils.split(path, "/");
            for (String item : paths) {
                if (StringUtils.isNumeric(item)) {
                    result = result.path(NumberUtils.toInt(item));
                } else {
                    result = result.path(item);
                }
                if (result.isMissingNode() || result.isNull()) {
                    break;
                }
            }
        } catch (Exception e) {
            logger.error("pathSelectNode error:", e);
        }
        return result;
    }

    /**
     * 将JSON串解析成JsonNode并获取路径下的节点
     * 
     * @param json JSON字符串
     * @return
     * @throws Exception
     */
    public static JsonNode readTree(String json, String path) {
        JsonNode result = null;
        try {
            result = pathSelectNode(objectMapper.readTree(json), path);
        } catch (Exception e) {
            logger.error("parse json readTree error:", e);
        }
        return result;
    }

    /**
     * 将JSON串解析成JsonNode
     * 
     * @param json JSON字符串
     * @return
     * @throws Exception
     */
    public static JsonNode readTree(String json) {
        return readTree(json, null);
    }

    /**
     * 将JSON串解析成对象 TypeReference<T> r = new TypeReference<T>(){} ;
     * 
     * @param json JSON字符串
     * @param classes 对象类型
     * @return
     * @throws Exception
     */
    public static <T> T readObject(String json, TypeReference<T> valueTypeRef) {
        T result = null;
        try {
            result = objectMapper.readValue(json, valueTypeRef);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    /**
     * 将JSON串解析成对象
     * 
     * @param json JSON字符串
     * @param classes 对象类型
     * @return
     * @throws Exception
     */
    public static <T> T readObject(String json, Class<T> classes) {
        T result = null;
        try {
            result = objectMapper.readValue(json, classes);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    /**
     * 将JSON串解析成对象
     * 
     * @param json JSON字符串
     * @param classes 对象类型
     * @return
     * @throws Exception
     */
    public static  <T> Map<String, T> readMap(String json) {
        Map<String, T> result = null;
        try {
            MapTypeReference<T> mtr = new MapTypeReference<>();
            result = objectMapper.readValue(json, mtr);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    
    /**
     * 转换类型
     * 
     * @param json JSON字符串
     * @param classes 对象类型
     * @return
     * @throws Exception
     */
    public static <T> Map<String, T> convertToMap(Object source) {
        Map<String, T> result = null;
        try {
            MapTypeReference<T> mtr = new MapTypeReference<>();
            result = objectMapper.readValue(objectMapper.writeValueAsString(source), mtr);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    /**
     * 转换类型
     * 
     * @param json JSON字符串
     * @param classes 对象类型
     * @return
     * @throws Exception
     */
    public static <T> T convertObject(Object source, Class<T> classes) {
        T result = null;
        try {
            result = objectMapper.readValue(objectMapper.writeValueAsString(source), classes);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    /**
     * 将JSON串解析成对象
     * 
     * @param json JSON字符串
     * @param classes 对象类型
     * @return
     * @throws Exception
     */
    public static <T> T readObject(File file, Class<T> classes) {
        T result = null;
        try {
            result = objectMapper.readValue(file, classes);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    /**
     * 将JSON串解析成对象
     * 
     * @param json JSON字符串
     * @param classes 对象类型
     * @return
     * @throws Exception
     */
    public static <T> T readObject(InputStream in, Class<T> classes) {
        T result = null;
        try {
            result = objectMapper.readValue(in, classes);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    /**
     * 将JSON串解析成对象
     * 
     * @param json JSON字符串
     * @param classes 对象类型
     * @return
     * @throws Exception
     */
    public static <T> List<T> readList(String jsonStr, Class<T> classes) {
        List<T> result = null;
        try {
            JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(List.class, classes);
            result = objectMapper.readValue(jsonStr, javaType);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    /**
     * 将JSON串解析成对象
     * 
     * @param json JSON字符串
     * @param classes 对象类型
     * @return
     * @throws Exception
     */
    public static <T> List<T> readList(File file, Class<T> classes) {
        List<T> result = null;
        try {
            JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(List.class, classes);
            result = objectMapper.readValue(file, javaType);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    /**
     * 将JSON串解析成对象
     * 
     * @param json JSON字符串
     * @param classes 对象类型
     * @return
     * @throws Exception
     */
    public static <T> List<T> readList(InputStream in, Class<T> classes) {
        List<T> result = null;
        try {
            ListTypeReference<T> lr = new ListTypeReference<T>();
            // JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(List.class, classes);
            result = objectMapper.readValue(in, lr);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    /**
     * 将对象串解析成JSON字符串
     * 
     * @throws Exception
     */
    public static String writeObject(Object object) {
        String result = null;
        try {
            result = objectMapper.writeValueAsString(object);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
        return result;
    }

    /**
     * 将对象串写到文件
     * 
     * @throws Exception
     */
    public static void writeObject(File file, Object object) {
        try {
            objectMapper.writeValue(file, object);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
    }

    /**
     * 将对象串写到文件
     * 
     * @throws Exception
     */
    public static void writeObject(OutputStream out, Object object) {
        try {
            objectMapper.writeValue(out, object);
        } catch (Exception e) {
            logger.error("parse json object error:", e);
        }
    }

    public static class MapTypeReference<T> extends TypeReference<Map<String, T>> {

    }

    public static class ListTypeReference<T> extends TypeReference<List<T>> {

    }

}
